#ifndef ZCOLVIEW_INL
#define ZCOLVIEW_INL
#include "ZColView.h"

/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
///                            ColView类的过滤函数                     ////
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////


template<typename StringType, typename T>
std::vector<std::reference_wrapper<T>> NDFrame::ColView<StringType, T>::values() const {
	std::vector<std::reference_wrapper<T>> result;
	for (size_t i = 0; i < this->m_df.size(); ++i) {
		result.push_back(std::ref(this->m_df.getItemPtr()[i].*member));
	}
	return result;
}


template<typename StringType, typename T>
void NDFrame::ColView<StringType, T>::sub(int num) {
	static_assert(std::is_same_v<T, int>, "sub() function is only available for int members.");
	for (size_t i = 0; i < this->m_df.size(); ++i) {
		this->m_df.getItemPtr()[i].*member -= num;
		if (this->m_df.getItemPtr()[i].*member < 0) {
			this->m_df.getItemPtr()[i].*member = 0;
		}
	}
}


template<typename StringType, typename T>
void NDFrame::ColView<StringType, T>::add(int num, int max_num) {
	static_assert(std::is_same_v<T, int>, "add() function is only available for int members.");
	for (size_t i = 0; i < this->m_df.size(); ++i) {
		this->m_df.getItemPtr()[i].*member += num;
		if (this->m_df.getItemPtr()[i].*member > max_num) {
			this->m_df.getItemPtr()[i].*member = max_num;
		}
	}
}


template<typename StringType, typename T>
float NDFrame::ColView<StringType, T>::mean_col() const {
	static_assert(std::is_same_v<T, int>, "mean_col() function is only available for int members.");
	auto values = this->values();
	if (values.empty()) {
		throw std::runtime_error("Cannot calculate mean_col of an empty column.");
	}
	float sum = std::accumulate(values.begin(), values.end(), 0.0f,
		[](float acc, const std::reference_wrapper<T>& value) {
			return acc + static_cast<float>(value.get());
		});
	return sum / values.size();
}


template<typename StringType, typename T>
size_t NDFrame::ColView<StringType, T>::max_col() const {
	static_assert(std::is_same_v<T, int>, "max_col() function is only available for int members.");
	int max_value = std::numeric_limits<int>::min();

	for (size_t i = 0; i < this->m_df.size(); ++i) {
		max_value = std::max(max_value, this->m_df.getItemPtr()[i].*member);
	}
	return max_value;
}


template<typename StringType, typename T>
size_t NDFrame::ColView<StringType, T>::min_col() const {
	static_assert(std::is_same_v<T, int>, "min_col() function is only available for int members.");
	int min_value = std::numeric_limits<int>::max();

	for (size_t i = 0; i < this->m_df.size(); ++i) {
		min_value = std::min(min_value, this->m_df.getItemPtr()[i].*member);
	}

	return min_value;
}


template<typename StringType, typename T>
auto NDFrame::ColView<StringType, T>::len() const -> std::vector<size_t> {
	static_assert(std::is_same_v<T, int>, "sort() function is only available for int members.");
	std::vector<size_t> lens;
	lens.reserve(this->m_df.size());
	for (size_t i = 0; i < this->m_df.size(); ++i) {
		lens.emplace_back(this->m_df.getItemPtr()[i].*member.size());
	}
	return lens;
}


template<typename StringType, typename T>
void NDFrame::ColView<StringType, T>::sort(bool descending) {
	static_assert(std::is_same_v<T, int>, "sort() function is only available for int members.");

	if (this->m_df.empty()) {
		throw std::runtime_error("Cannot sort an empty column.");
	}

	std::vector<ItemType*> items(this->m_df.size());
	for (size_t i = 0; i < this->m_df.size(); ++i) {
		items[i] = &this->m_df.getItemPtr()[i];
	}

	auto compare = [this](const ItemType* a, const ItemType* b) -> bool {
		return a->*member < b->*member;
	};

	if (descending) {
		std::sort(items.begin(), items.end(), [this](const ItemType* a, const ItemType* b) {
			return a->*member > b->*member;
			});
	}
	else {
		std::sort(items.begin(), items.end(), compare);
	}

	std::vector<ItemType> sortedItems(this->m_df.size());
	for (size_t i = 0; i < this->m_df.size(); ++i) {
		sortedItems[i] = *items[i];
	}

	for (size_t i = 0; i < this->m_df.size(); ++i) {
		this->m_df.getItemPtr()[i] = sortedItems[i];
	}

}


template<typename StringType, typename T>
void NDFrame::ColView<StringType, T>::replace(CharType oldChar, CharType newChar) {
	static_assert(std::is_same_v<T, StringType>, "replace() function is only available for StringType members.");
	if (this->m_df.empty()) {
		return;
	}

	for (size_t i = 0; i < this->m_df.size(); ++i) {
		StringType& modified_text = this->m_df.getItemPtr()[i].*member;
		std::replace(modified_text.begin(), modified_text.end(), oldChar, newChar);
	}
}

template<typename StringType, typename T>
void NDFrame::ColView<StringType, T>::replace(CharType oldChar0, CharType newChar0, CharType oldChar1,
	CharType newChar1) {
	static_assert(std::is_same_v<T, StringType>, "replace() function is only available for StringType members.");
	if (this->m_df.empty()) {
		return;
	}

	for (size_t i = 0; i < this->m_df.size(); ++i) {
		StringType& modified_text = this->m_df.getItemPtr()[i].*member;
		for (auto& j : modified_text) {
			if (j == oldChar0) {
				j = newChar0;
			}
			else if (j == oldChar1) {
				j = newChar1;
			}
		}
	}
}


template<typename StringType, typename T>
template<typename MapType>
void NDFrame::ColView<StringType, T>::multi_replace_df_text_impl(const MapType& replacements) {
	static_assert(std::is_same_v<T, StringType>, "df_text() function is only available for StringType members.");
	if (this->m_df.empty()) {
		return;
	}

	for (size_t i = 0; i < this->m_df.size(); ++i) {
		StringType& modified_text = this->m_df.getItemPtr()[i].*member;
		for (const auto& replacement : replacements) {
			const StringType& from = replacement.first;
			const StringType& to = replacement.second;
			size_t pos = 0;
			while ((pos = modified_text.find(from, pos)) != StringType::npos) {
				modified_text.replace(pos, from.length(), to);
				pos += to.length();
			}
		}
	}
}


template<typename StringType, typename T>
StringType NDFrame::ColView<StringType, T>::join(const StringType& Char_) const {
	static_assert(std::disjunction_v<std::is_same<T, std::string>, std::is_same<T, std::wstring>>,
		"len() function is only available for std::string or std::wstring members.");
	if (this->m_df.size() == 0) {
		return {};
	}
	if (Char_.size() > 1) {
		return {};
	}

	std::basic_ostringstream<CharType> oss;
	for (size_t i = 0; i < this->m_df.size(); ++i) {
		if (i != 0 && !Char_.empty()) {
			oss << Char_;
		}
		oss << this->m_df.getItemPtr()[i].*member;
	}
	return oss.str();
}


template<typename StringType, typename T>
StringType NDFrame::ColView<StringType, T>::join_replace(CharType oldChar, const CharType* newChar) const {
	static_assert(std::disjunction_v<std::is_same<T, std::string>, std::is_same<T, std::wstring>>,
		"len() function is only available for std::string or std::wstring members.");
	if (this->m_df.size() == 0) {
		return {};
	}
	if (strlen(newChar) > 1) {
		return {};
	}

	std::basic_ostringstream<CharType> oss;
	for (size_t i = 0; i < this->m_df.size(); ++i) {
		for (auto c : this->m_df.getItemPtr()[i].*member) {
			if (c == oldChar) {
				oss << newChar;
			}
			else {
				oss << c;
			}
		}
	}
	return oss.str();
}


template<typename StringType, typename T>
StringType
NDFrame::ColView<StringType, T>::join_replace(CharType oldChar, CharType newChar, const StringType& Char_) const {
	if (this->m_df.size() == 0) {
		return {};
	}
	if (Char_.size() > 1) {
		return {};
	}

	std::basic_ostringstream<CharType> oss;
	for (size_t i = 0; i < this->m_df.size(); ++i) {
		if (i != 0 && !Char_.empty()) {
			oss << Char_;
		}
		for (auto c : this->m_df.getItemPtr()[i].*member) {
			if (c == oldChar) {
				oss << newChar;
			}
			else {
				oss << c;
			}
		}
	}
	return oss.str();
}


template<typename StringType, typename T>
StringType NDFrame::ColView<StringType, T>::join_replace2(CharType oldChar0, CharType newChar0, CharType oldChar1,
	CharType newChar1,
	const StringType& Char_) const {
	if (this->m_df.size() == 0) {
		return {};
	}
	if (Char_.size() >= 2) {
		return {};
	}

	std::basic_ostringstream<CharType> oss;
	for (size_t i = 0; i < this->m_df.size(); ++i) {
		if (i != 0 && !Char_.empty()) {
			oss << Char_;
		}
		for (auto c : this->m_df.getItemPtr()[i].*member) {
			if (c == oldChar0) {
				oss << newChar0;
			}
			else if (c == oldChar1) {
				oss << newChar1;
			}
			else {
				oss << c;
			}
		}
	}
	return oss.str();
}


template<typename StringType, typename T>
bool NDFrame::ColView<StringType, T>::ptr_reg_impl(VecItemPtr& out_ptr,
	const std::vector<RegexType>& regex_) const {
	static_assert(std::is_same_v<T, StringType>, "regex() function is only available for StringType members.");
	if (out_ptr.empty()) { out_ptr.reserve(3); }
	if (this->m_df.empty()) return false;

	for (size_t i = 0; i < this->m_df.size(); ++i) {
		for (const auto& reg : regex_) {
			if (matches_regex(this->m_df.getItemPtr()[i].*member, reg)) {
				out_ptr.push_back(&(this->m_df.getItemPtr()[i]));
				break;
			}
		}
	}
	return !out_ptr.empty();
}


template<typename StringType, typename T>
bool NDFrame::ColView<StringType, T>::ptr_reg_common4(
	VecItemPtr& out_ptr,
	const std::vector<RegexType>& regex_, int len,
	const std::function<bool(size_t, size_t)>& compare) const {
	static_assert(std::is_same_v<T, StringType>, "regex() function is only available for StringType members.");
	if (out_ptr.empty()) { out_ptr.reserve(3); }
	if (this->m_df.empty()) return false;

	for (size_t i = 0; i < this->m_df.size(); ++i) {
		const StringType& field_value = this->m_df.getItemPtr()[i].*member;
		if (compare(field_value.size(), len)) {
			for (const auto& reg : regex_) {
				if (matches_regex(field_value, reg)) {
					out_ptr.push_back(&this->m_df.getItemPtr()[i]);
					break;
				}
			}
		}
	}
	return !out_ptr.empty();
}


template<typename StringType, typename T>
bool NDFrame::ColView<StringType, T>::ptr_reg_common3(
	VecItemPtr& out_ptr,
	const std::vector<RegexType>& regex_,
	const std::function<bool(const ItemType&)>& condition
) const {
	static_assert(std::is_same_v<T, StringType>, "regex() function is only available for StringType members.");
	if (out_ptr.empty()) { out_ptr.reserve(3); }
	if (this->m_df.empty()) return false;
	for (size_t i = 0; i < this->m_df.size(); ++i) {
		if (condition(this->m_df.getItemPtr()[i])) {
			const StringType& field_value = this->m_df.getItemPtr()[i].*member;
			for (const auto& reg : regex_) {
				if (matches_regex(field_value, reg)) {
					out_ptr.push_back(&this->m_df.getItemPtr()[i]);
					break;
				}
			}
		}
	}
	return !out_ptr.empty();
}


template<typename StringType, typename T>
bool NDFrame::ColView<StringType, T>::ptr2_reg_impl(VecItemPtr& inout_ptr,
	const std::vector<RegexType>& regex_) const {
	static_assert(std::is_same_v<T, StringType>, "regex() function is only available for StringType members.");
	if (inout_ptr.empty()) return false;

	std::vector<ItemType*> filtered_pointers;
	filtered_pointers.reserve(inout_ptr.size());

	for (size_t i = 0; i < this->m_df.size(); ++i) {
		if (std::find(inout_ptr.cbegin(), inout_ptr.cend(), &this->m_df.getItemPtr()[i]) == inout_ptr.cend()) continue;
		const StringType& field_value = this->m_df.getItemPtr()[i].*member;
		for (const auto& reg : regex_) {
			if (matches_regex(field_value, reg)) {
				filtered_pointers.push_back(&this->m_df.getItemPtr()[i]);
				break;
			}
		}
	}
	inout_ptr = std::move(filtered_pointers);
	return !inout_ptr.empty();
}


template<typename StringType, typename T>
bool NDFrame::ColView<StringType, T>::ptr2_reg_common4(
	VecItemPtr& inout_ptr,
	const std::vector<RegexType>& regex_, int len,
	const std::function<bool(size_t, size_t)>& compare) const {
	static_assert(std::is_same_v<T, StringType>, "regex() function is only available for StringType members.");
	if (inout_ptr.empty()) return false;

	std::vector<ItemType*> filtered_pointers;
	filtered_pointers.reserve(inout_ptr.size());

	for (size_t i = 0; i < this->m_df.size(); ++i) {
		if (std::find(inout_ptr.cbegin(), inout_ptr.cend(), &this->m_df.getItemPtr()[i]) == inout_ptr.cend()) continue;
		const StringType& field_value = this->m_df.getItemPtr()[i].*member;
		if (compare(field_value.size(), len)) {
			for (const auto& reg : regex_) {
				if (matches_regex(field_value, reg)) {
					filtered_pointers.push_back(&this->m_df.getItemPtr()[i]);
					break;
				}
			}
		}
	}
	inout_ptr = std::move(filtered_pointers);
	return !inout_ptr.empty();
}


template<typename StringType, typename T>
bool NDFrame::ColView<StringType, T>::ptr2_reg_common3(
	VecItemPtr& inout_ptr,
	const std::vector<RegexType>& regex_,
	const std::function<bool(const ItemType&)>& condition
) const {
	static_assert(std::is_same_v<T, StringType>, "regex() function is only available for StringType members.");
	if (inout_ptr.empty()) return false;

	std::vector<ItemType*> filtered_pointers;
	filtered_pointers.reserve(inout_ptr.size());

	for (size_t i = 0; i < this->m_df.size(); ++i) {
		if (std::find(inout_ptr.cbegin(), inout_ptr.cend(), &this->m_df.getItemPtr()[i]) == inout_ptr.cend()) continue;

		if (condition(this->m_df.getItemPtr()[i])) {
			const StringType& field_value = this->m_df.getItemPtr()[i].*member;
			for (const auto& reg : regex_) {
				if (matches_regex(field_value, reg)) {
					filtered_pointers.push_back(&this->m_df.getItemPtr()[i]);
					break;
				}
			}
		}
	}

	inout_ptr = std::move(filtered_pointers);
	return !inout_ptr.empty();
}


////////////////////////////////////////////////////////////////////////////////
/////                   Cross class function return ZDataFrame
////////////////////////////////////////////////////////////////////////////////
template<typename StringType, typename T>
NDFrame::ZDataBase<StringType>
NDFrame::ColView<StringType, T>::DF_reg_impl(const std::vector<RegexType>& regex_) const {
	static_assert(std::is_same_v<T, StringType>, "regex() function is only available for StringType members.");
	NDFrame::ZDataFrame<StringType> result(m_df.capacity() > 3 ? 3 : m_df.capacity());
	if (this->m_df.size() == 0) return result;

	for (size_t i = 0; i < this->m_df.size(); ++i) {
		for (const auto& reg : regex_) {
			const StringType& field_value = m_df.getItemPtr()[i].*member;
			if (matches_regex(field_value, reg)) {
				result.push_back(m_df.getItemPtr()[i]);
				break;
			}
		}
	}
}


template<typename StringType, typename T>
NDFrame::ZDataBase<StringType> NDFrame::ColView<StringType, T>::DF_reg_common3(
	const std::vector<RegexType>& regex_, int len,
	const std::function<bool(size_t, size_t)>& compare) const {

	static_assert(std::is_same_v<T, StringType>, "regex() function is only available for StringType members.");

	NDFrame::ZDataFrame<StringType> result(m_df.capacity() > 3 ? 3 : m_df.capacity());
	if (this->m_df.size() == 0) return result;

	for (size_t i = 0; i < this->m_df.size(); ++i) {
		const StringType& field_value = this->m_df.getItemPtr()[i].*member;
		if (compare(field_value.size(), len)) {
			for (const auto& reg : regex_) {
				if (matches_regex(field_value, reg)) {
					result.push_back(m_df.getItemPtr()[i]);
					break;
				}
			}
		}
	}
	return result;
}


template<typename StringType, typename T>
NDFrame::ZDataBase<StringType> NDFrame::ColView<StringType, T>::DF_reg_common2(
	const std::vector<RegexType>& regex_,
	const std::function<bool(const ItemType&)>& condition) const {
	static_assert(std::is_same_v<T, StringType>, "regex() function is only available for StringType members.");
	ZDataFrame<StringType> result(m_df.capacity() > 3 ? 3 : m_df.capacity());
	if (this->m_df.size() == 0) return result;

	for (size_t i = 0; i < this->m_df.size(); ++i) {
		if (condition(this->m_df.getItemPtr()[i])) {
			const StringType& field_value = this->m_df.getItemPtr()[i].*member;
			for (const auto& reg : regex_) {
				if (matches_regex(field_value, reg)) {
					result.push_back(this->m_df.getItemPtr()[i]);
					break;
				}
			}
		}
	}

	return result;
}

#endif